<?php

namespace app\admin\library;

use app\admin\model\auth\Admin;
use think\facade\Db;
use think\facade\Config;
use think\facade\Request;
use think\facade\Validate;
use app\common\library\token\Token;
use general\Random;

class Auth extends \general\Auth
{
    protected $_error = '';
    protected $_logined = false;
    protected $_admin = null;
    protected $_token = '';
    //Token默认有效时长
    protected $keeptime = 2592000;
    protected $requestUri = '';

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * 获取Admin模型
     * @return Admin
     */
    public function getAdmin()
    {
        return $this->_admin;
    }

    /**
     * 兼容调用admin模型的属性
     * @param string $name
     * @return mixed
     */
    public function __get($name)
    {
        return $this->_admin ? $this->_admin->$name : NULL;
    }

    /**
     * 兼容调用admin模型的属性
     */
    public function __isset($name)
    {
        return isset($this->_admin) ? isset($this->_admin->$name) : false;
    }

    /**
     * 根据Token初始化
     * @param string $token Token
     * @return boolean
     */
    public function init($token)
    {
        if ($this->_logined) {
            return true;
        }
        if ($this->_error) {
            return false;
        }
        $data = Token::get($token);
        if (!$data) {
            return false;
        }
        $admin_id = intval($data['admin_id']);
        if ($admin_id > 0) {
            $admin = Admin::find($admin_id);
            if (!$admin) {
                $this->setError('Username is incorrect');
                return false;
            }
            if ($admin['status'] == 0) {
                $this->setError('Admin is forbidden');
                return false;
            }
            $this->_admin = $admin;
            $this->_logined = true;
            $this->_token = $token;

            return true;
        } else {
            $this->setError('You are not logged in');
            return false;
        }
    }

    /**
     * 用户登录
     * @param string $username 用户名
     * @param string $password 密码
     * @return boolean
     */
    public function login($username, $password)
    {
        $admin = Admin::where('username', '=', $username)->find();
        if (!$admin) {
            $this->setError('Username is incorrect');
            return false;
        }
        if ($admin['status'] == 0) {
            $this->setError('Admin is forbidden');
            return false;
        }
        if ($admin->login_failure >= 10 && time() - $admin->updatetime < 86400) {
            $this->setError('Please try again after 1 day');
            return false;
        }
        if ($admin->password != $this->getEncryptPassword($password, $admin->salt)) {
            //登陆失败次数
            $admin->login_failure++;
            $admin->save();
            $this->setError('Password is incorrect');
            return false;
        }

        //直接登录会员
        return $this->direct($admin->id);
    }

    /**
     * 退出
     * @return boolean
     */
    public function logout()
    {
        if (!$this->_logined) {
            $this->setError('You are not logged in');
            return false;
        }
        //设置登录标识
        $this->_logined = false;
        //删除Token
        Token::delete($this->_token);
        return true;
    }

    /**
     * 修改密码
     * @param string $newpassword 新密码
     * @param string $oldpassword 旧密码
     * @param bool $ignoreoldpassword 忽略旧密码
     * @return boolean
     */
    public function changepwd($newpassword, $oldpassword = '', $ignoreoldpassword = false)
    {
        if (!$this->_logined) {
            $this->setError('You are not logged in');
            return false;
        }
        //判断旧密码是否正确
        if ($this->_admin->password == $this->getEncryptPassword($oldpassword, $this->_admin->salt) || $ignoreoldpassword) {
            Db::startTrans();
            try {
                $salt = Random::alnum();
                $newpassword = $this->getEncryptPassword($newpassword, $salt);
                $this->_admin->save(['login_failure' => 0, 'password' => $newpassword, 'salt' => $salt]);
                Token::delete($this->_token);
                Db::commit();
            } catch (\Exception $e) {
                Db::rollback();
                $this->setError($e->getMessage());
                return false;
            }
            return true;
        } else {
            $this->setError('Password is incorrect');
            return false;
        }
    }

    /**
     * 直接登录账号
     * @param int $admin_id
     * @return boolean
     */
    public function direct($admin_id)
    {
        $admin = Admin::find($admin_id);
        if ($admin) {
            Db::startTrans();
            try {
                $ip = request()->ip();
                $time = time();

                //记录本次登录的IP和时间
                $admin->login_ip = $ip;
                $admin->login_time = $time;
                //重置登录失败次数
                $admin->login_failure = 0;

                $admin->save();

                $this->_admin = $admin;

                $this->_token = Random::uuid();
                Token::set($this->_token, $admin->id, $this->keeptime);

                $this->_logined = true;

                Db::commit();
            } catch (\Exception $e) {
                Db::rollback();
                $this->setError($e->getMessage());
                return false;
            }
            return true;
        } else {
            return false;
        }
    }

    public function check($name, $uid = '', $relation = 'or', $mode = 'url')
    {
        $uid = $uid ? $uid : $this->id;
        return parent::check($name, $uid, $relation, $mode);
    }

    /**
     * 判断是否登录
     * @return boolean
     */
    public function isLogin()
    {
        if ($this->_logined) {
            return true;
        }
        return false;
    }

    /**
     * 获取当前Token
     * @return string
     */
    public function getToken()
    {
        return $this->_token;
    }

    /**
     * 获取密码加密后的字符串
     * @param string $password 密码
     * @param string $salt 密码盐
     * @return string
     */
    public function getEncryptPassword($password, $salt = '')
    {
        return md5(md5($password) . $salt);
    }

    /**
     * 获取当前请求的URI
     * @return string
     */
    public function getRequestUri()
    {
        return $this->requestUri;
    }

    /**
     * 设置当前请求的URI
     * @param string $uri
     */
    public function setRequestUri($uri)
    {
        $this->requestUri = $uri;
    }

    /**
     * 检测当前控制器和方法是否匹配传递的数组
     *
     * @param array $arr 需要验证权限的数组
     * @return boolean
     */
    public function match($arr = [])
    {
        $request = Request::instance();
        $arr = is_array($arr) ? $arr : explode(',', $arr);
        if (!$arr) {
            return false;
        }
        $arr = array_map('strtolower', $arr);
        // 是否存在
        if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr)) {
            return true;
        }

        // 没找到匹配
        return false;
    }

    public function getRuleList($uid = null)
    {
        $uid = is_null($uid) ? $this->id : $uid;
        return parent::getRuleList($uid);
    }

    public function getRuleIds($uid = null)
    {
        $uid = is_null($uid) ? $this->id : $uid;
        return parent::getRuleIds($uid);
    }

    public function getGroups($uid = null)
    {
        $uid = is_null($uid) ? $this->id : $uid;
        return parent::getGroups($uid);
    }

    public function getGroupIds($uid = null)
    {
        $uid = is_null($uid) ? $this->id : $uid;
        $groups = $this->getGroups($uid);
        $groupIds = [];
        foreach ($groups as $K => $v) {
            $groupIds[] = (int)$v['group_id'];
        }
        return $groupIds;
    }

    public function getUserInfo($uid = null)
    {
        $uid = is_null($uid) ? $this->id : $uid;

        return $uid != $this->id ? Admin::find($uid) : $this->_admin;
    }

    public function isSuperAdmin()
    {
        return in_array('*', $this->getRuleIds()) ? true : false;
    }


    /**
     * 获得面包屑导航
     * @param string $path
     * @return array
     */
    public function getBreadCrumb($path = '')
    {
        if ($this->breadcrumb || !$path) {
            return $this->breadcrumb;
        }
        $titleArr = [];
        $menuArr = [];
        $urlArr = explode(':', $path);
        foreach ($urlArr as $index => $item) {
            $pathArr[implode(':', array_slice($urlArr, 0, $index + 1))] = $index;
        }
        if (!$this->rules && $this->id) {
            $this->getRuleList();
        }
        //获取所有权限-否则用户无权限时日志不记录
        $ruleList = Db::name($this->config['auth_rule'])
            ->where('status', '=', 1)
            ->field('id,pid,condition,icon,name,title')
            ->cache('all_rule_list',30)
            ->select();
        foreach ($ruleList as $rule) {
            if (isset($pathArr[$rule['name']])) {
                $rule['title'] = $rule['title'];
                $rule['url'] = url($rule['name']);
                $titleArr[$pathArr[$rule['name']]] = $rule['title'];
                $menuArr[$pathArr[$rule['name']]] = $rule;
            }

        }
        ksort($menuArr);
        $this->breadcrumb = $menuArr;
        return $this->breadcrumb;
    }

    public function getMenuList()
    {
        $userRule = $this->getRuleList();

        $menuList = Db::name('auth_rule')
            ->where('status', '=', 1)
            ->where('ismenu', '=', 1)
            ->order(['weigh' => 'desc', 'id' => 'asc'])
            ->select();
        foreach ($menuList as $k => &$v) {
            if (!in_array($v['name'], $userRule)) {
                unset($menuList[$k]);
                continue;
            }
        }
        return $this->getTreeMenu($menuList);
    }

    public function getRoleList()
    {
        return array_values($this->getRuleList());
    }

    public function getGroupList()
    {
        $groupList = Db::name('auth_group')
            ->field('id,title')
            ->where('status', '=', 1)
            ->order('id', 'asc')
            ->select();
        return $groupList;
    }

    /**
     * 获取树状菜单列表
     * @param $menuList
     * @param int $parentId
     * @return array
     */
    protected function getTreeMenu($menuList, int $pid = 0)
    {
        $data = [];
        foreach ($menuList as $key => $item) {
            if ($item['pid'] == $pid) {
                $children = $this->getTreeMenu($menuList, $item['id']);
                $route = [];
                $route['path'] = $item['path'];
                $route['name'] = $item['name'];
                $route['component'] = $item['component'];

                $meta = [];
                $meta['title'] = $item['title'];
                $meta['ignoreKeepAlive'] = $item['keepalive'] == 1 ? false : true;
                $meta['hideMenu'] = $item['show'] == 1 ? false : true;
                $meta['icon'] = $item['icon'];
                $meta['orderNo'] = $item['weigh'];
                if (substr($item['path'], 0, 4) == 'http') {
                    $meta['frameSrc'] = $item['path'];
                }
                $route['meta'] = $meta;

                if (!empty($children)) {
                    $route['redirect'] = $item['path'] . "/" . $children[0]['path'];
                    $route['children'] = $children;
                }
                $data[] = $route;
                unset($menuList[$key]);
            }
        }
        return $data;
    }

    /**
     * 设置错误信息
     *
     * @param string $error 错误信息
     * @return Auth
     */
    public function setError($error)
    {
        $this->_error = $error;
        return $this;
    }

    /**
     * 获取错误信息
     * @return string
     */
    public function getError()
    {
        return $this->_error ? lang($this->_error) : '';
    }
}